<template>
  <!-- state.count++不是响应式的 页面中state.count值不会更新-->
  <div @click="state.count++">shallowRef:{{ state.count }}</div>
  <div @click="handleClick1">shallowRef:{{ state.count }}</div>
  <!-- state = { count: 2 }是响应式的 页面中state.count值会更新-->
  <div @click="state = { count: 2 }">shallowRef:{{ state.count }}</div>
  <div @click="handleClick2">shallowRef:{{ state.count }}</div>

  <div @click="handleClick3">{{ shallow.greet }}</div>

  <input v-model="text" />
  <!-- 实现类似防抖的功能，input中的text被修改时由于操作比较快，h1中的text值不会时时更新，停止操作200ms之后会更新 -->
  <h1>{{ text }}</h1>

  <div @click="stateReactive.foo++">{{ stateReactive.foo }}</div>
  <div @click="stateReactive.nested.bar++">{{ stateReactive.nested.bar }}</div>

  <div @click="stateReadonly.foo++">{{ stateReadonly.foo }}</div>
  <div @click="stateReadonly.nested = { bar: 500 }">
    {{ stateReadonly.nested.bar }}
  </div>
  <div @click="stateReadonly.nested.bar++">{{ stateReadonly.nested.bar }}</div>

  <div @click="handleMarkRaw">
    {{ p.likeFood.fruits.apple }}
  </div>
</template>

<script setup>
import {
  shallowRef,
  triggerRef,
  watchEffect,
  customRef,
  shallowReactive,
  isReactive,
  shallowReadonly,
  isReadonly,
  reactive,
  toRaw,
  markRaw,
  effectScope,
  ref,
  computed,
  watch,
} from "vue";
/*shallowRef生成的数据只有第一层key是响应式的，因为ref数据有一个特点，会默认加一个value属性，
传入的对象默认会作为value的属性值，所有用shallowRef生成的响应式数据只有value属性是响应的，
是能达到响应式效果的，shallowRef对象中的属性不是响应式的*/
// shallowRef用法

const state = shallowRef({ count: 1 });
const handleClick1 = () => {
  //state.value.count中的count属性不会收集依赖，count属性值被修改的时候页面中state.count(template中不需要写.value)值不会更新
  state.value.count++;
  console.log(state.value.count, "00000");
};
const handleClick2 = () => {
  //state.value中的value属性会收集依赖，value属性值被修改的时候页面中state.count(template中不需要写.value)值会更新
  state.value = { count: 2 };
  console.log(state.value, "11111");
};

// triggerRef通常和shallowRef一起使用
const shallow = shallowRef({
  greet: "Hello, world",
});
watchEffect(() => {
  console.log(shallow.value.greet, "2222222");
});
const handleClick3 = () => {
  shallow.value.greet = "Hello, universe";
  triggerRef(shallow); //triggerRef的作用则是手动执行与 shallowRef 关联的任何副作用，强制更新视图
};

//customRef实现防抖的功能
const useDebouncedRef = (value, delay = 200) => {
  let timeout;
  return customRef((track, trigger) => {
    return {
      get() {
        track();
        return value;
      },
      set(newValue) {
        clearTimeout(timeout);
        timeout = setTimeout(() => {
          value = newValue;
          trigger();
        }, delay);
      },
    };
  });
};
const text = useDebouncedRef("hello");

//shallowReactive的用法
const stateReactive = shallowReactive({
  foo: 1,
  nested: {
    bar: 2,
  },
});
stateReactive.foo++;
isReactive(stateReactive.nested); // false
stateReactive.nested.bar++;

//shallowReadonly的用法只有浅层(第一层foo,nested)数据是可读的,深层(bar)的数据一样是响应式可修改的
const dataReactive = reactive({
  foo: 1,
  nested: {
    bar: 2,
  },
});
const stateReadonly = shallowReadonly(dataReactive);
stateReadonly.foo++;
isReadonly(stateReadonly.nested); // false
stateReadonly.nested.bar++;
/*
toRaw()可以从由reactive()、readonly()、shalloreactive()或shalloreadonly()创建的代理返回原始对象。
可用于临时读取而不产生代理访问/跟踪开销，或用于写入而不触发更改。
不建议保留对原始对象的持久引用。小心使用
*/
//toRaw的使用 实际项目应用不推荐时用toRaw 不建议保留对原始对象foo的持久引用。小心使用
const foo = {};
const reactiveFoo = reactive(foo);
//foo和reactiveFoo不存在引用关系 所以后续对这个普通对象的操作均不会对原响应式对象产生影响
console.log(toRaw(reactiveFoo) === foo, foo == reactiveFoo); // true false
//markRaw使用
let p = reactive({
  name: "张三",
  age: 18,
});

/*
markRaw
作用：标记一个对象，使其永远不会再成为响应式对象。

应用场景:
有些值不应被设置为响应式的，例如复杂的第三方类库等。

当渲染具有不可变数据源的大列表时，跳过响应式转换可以提高性能。
 */
// 在p对象上直接添加一个对象会处理为响应式的 所以使用markRaw()使其永远不会再成为响应式对象。
p.likeFood = markRaw({
  fruits: {
    apple: "苹果",
  },
});
const handleMarkRaw = () => {
  p.likeFood.fruits.apple = "ban"; //数据修改了，因为不是响应式的，所以视图不会更新
  console.log(p, "963255");
};

/*在Vue的setup中，computed,watch,watchEffect响应式依赖(也就是函数)会自动绑定到组件实例上，
当组件卸载时自动清理与组件绑定的响应式依赖，当我们在vue组件的setup外或者编写一个独立的包时，
使用computed,watch,watchEffect收集了依赖，如果忘记手动清理，将导致内存泄漏，如果手动清理写代码将会非常多且麻烦
有什么好的解决办法呢，这时就用到effectScope
*/
//effectScope用法如下，可以不在setup中使用，如果想相信了解用法看这个https://www.bilibili.com/video/BV1Lr4y1z78o
const scope = effectScope();
const counter = ref(1);
setInterval(() => {
  counter.value++;
}, 1000);
scope.run(() => {
  const doubled = computed(() => counter.value * 2);
  //外层scope关闭时,里层也会关闭，不需要手动调用里层的stop()
  effectScope().run(() => {
    watch(doubled, () => console.log(doubled.value, "633255"));
  });
  watchEffect(() => console.log("Count: ", doubled.value));
});
setTimeout(() => {
  // 将来在不需要的时候，可以手动控制响应式依赖副作用释放的时间
  scope.stop();
}, 4000);
</script>

<style lang="scss" scoped></style>
